package org.poem;

import net.marvin.framework.color.MarvinColorModelConverter;
import net.marvin.framework.image.MarvinImage;
import net.marvin.framework.io.MarvinImageIO;

import java.awt.*;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author Administrator
 */
public class ChromaToTransparency {
    private static final Integer STEP = 10;

    public ChromaToTransparency() {
        MarvinImage image = MarvinImageIO.loadImage("C:\\Users\\Administrator\\Desktop\\images\\5.png");
        MarvinImage imageOut = new MarvinImage(image.getWidth(), image.getHeight());
        grayImage(image, imageOut);
        MarvinImageIO.saveImage(imageOut, "C:\\Users\\Administrator\\Desktop\\images\\tailor\\images-1.png");
        MarvinImage binary = binaryImage(imageOut);
        MarvinImageIO.saveImage(binary, "C:\\Users\\Administrator\\Desktop\\images\\tailor\\images-2.png");
        MarvinImage imageOut1 = new MarvinImage(image.getWidth(), image.getHeight());
        transImages(image, binary, imageOut1);
        MarvinImageIO.saveImage(imageOut1, "C:\\Users\\Administrator\\Desktop\\images\\tailor\\images-3.png");
    }

    /**
     * 图片灰度处理
     *
     * @param bufferedImage 需要灰度处理的图片
     * @param imageOut      保存的图片
     */
    public static void grayImage(MarvinImage bufferedImage, MarvinImage imageOut) {
        for (int i = 0; i < bufferedImage.getWidth(); i++) {
            for (int j = 0; j < bufferedImage.getHeight(); j++) {
                final int color = bufferedImage.getIntColor(i, j);
                final int r = (color >> 16) & 0xff;
                final int g = (color >> 8) & 0xff;
                final int b = color & 0xff;
                int gray = (int) (0.3 * r + 0.59 * g + 0.11 * b);
                int newPixel = colorToRGB(255, gray, gray, gray);
                imageOut.setIntColor(i, j, newPixel);
            }
        }
    }

    /**
     * 颜色分量转换为RGB值
     *
     * @param alpha
     * @param red
     * @param green
     * @param blue
     * @return
     */
    private static int colorToRGB(int alpha, int red, int green, int blue) {
        int newPixel = 0;
        newPixel += alpha;
        newPixel = newPixel << 8;
        newPixel += red;
        newPixel = newPixel << 8;
        newPixel += green;
        newPixel = newPixel << 8;
        newPixel += blue;
        return newPixel;

    }

    /**
     * 把图片进行二进制改造
     *
     * @param image
     * @return
     */
    public static MarvinImage binaryImage(MarvinImage image) {
        int w = image.getWidth();
        int h = image.getHeight();
        float[] rgb = new float[3];
        double[][] point = new double[w][h];
        int black = new Color(0, 0, 0).getRGB();
        int white = new Color(255, 255, 255).getRGB();
        MarvinImage bi = new MarvinImage(w, h);
        for (int x = 0; x < w; x++) {
            for (int y = 0; y < h; y++) {
                int pixel = image.getIntColor(x, y);
                rgb[0] = (pixel & 0xff0000) >> 16;
                rgb[1] = (pixel & 0xff00) >> 8;
                rgb[2] = (pixel & 0xff);
                float avg = (rgb[0] + rgb[1] + rgb[2]) / 3;
                point[x][y] = avg;

            }
        }
        //这里是阈值，白底黑字还是黑底白字，大多数情况下建议白底黑字，后面都以白底黑字为例
        double SW = 60;
        for (int x = 0; x < w; x++) {
            for (int y = 0; y < h; y++) {
                if (point[x][y] < SW) {
                    bi.setIntColor(x, y, black);
                } else {
                    bi.setIntColor(x, y, white);
                }
            }
        }
        return denoise(bi);
    }

    /**
     * 降噪，以1个像素点为单位（实际使用中可以循环降噪，或者把单位可以扩大为多个像素点）
     *
     * @param image
     * @return
     */
    public static MarvinImage denoise(MarvinImage image) {
        int w = image.getWidth();
        int h = image.getHeight();
        int white = new Color(255, 255, 255).getRGB();

        if (isWhite(image.getIntColor(1, 0)) && isWhite(image.getIntColor(0, 1)) && isWhite(image.getIntColor(1, 1))) {
            image.setIntColor(0, 0, white);
        }
        if (isWhite(image.getIntColor(w - 2, 0)) && isWhite(image.getIntColor(w - 1, 1)) && isWhite(image.getIntColor(w - 2, 1))) {
            image.setIntColor(w - 1, 0, white);
        }
        if (isWhite(image.getIntColor(0, h - 2)) && isWhite(image.getIntColor(1, h - 1)) && isWhite(image.getIntColor(1, h - 2))) {
            image.setIntColor(0, h - 1, white);
        }
        if (isWhite(image.getIntColor(w - 2, h - 1)) && isWhite(image.getIntColor(w - 1, h - 2)) && isWhite(image.getIntColor(w - 2, h - 2))) {
            image.setIntColor(w - 1, h - 1, white);
        }

        for (int x = 1; x < w - 1; x++) {
            int y = 0;
            if (isBlack(image.getIntColor(x, y))) {
                int size = 0;
                if (isWhite(image.getIntColor(x - 1, y))) {
                    size++;
                }
                if (isWhite(image.getIntColor(x + 1, y))) {
                    size++;
                }
                if (isWhite(image.getIntColor(x, y + 1))) {
                    size++;
                }
                if (isWhite(image.getIntColor(x - 1, y + 1))) {
                    size++;
                }
                if (isWhite(image.getIntColor(x + 1, y + 1))) {
                    size++;
                }
                if (size >= 5) {
                    image.setIntColor(x, y, white);
                }
            }
        }
        for (int x = 1; x < w - 1; x++) {
            int y = h - 1;
            if (isBlack(image.getIntColor(x, y))) {
                int size = 0;
                if (isWhite(image.getIntColor(x - 1, y))) {
                    size++;
                }
                if (isWhite(image.getIntColor(x + 1, y))) {
                    size++;
                }
                if (isWhite(image.getIntColor(x, y - 1))) {
                    size++;
                }
                if (isWhite(image.getIntColor(x + 1, y - 1))) {
                    size++;
                }
                if (isWhite(image.getIntColor(x - 1, y - 1))) {
                    size++;
                }
                if (size >= 5) {
                    image.setIntColor(x, y, white);
                }
            }
        }

        for (int y = 1; y < h - 1; y++) {
            int x = 0;
            if (isBlack(image.getIntColor(x, y))) {
                int size = 0;
                if (isWhite(image.getIntColor(x + 1, y))) {
                    size++;
                }
                if (isWhite(image.getIntColor(x, y + 1))) {
                    size++;
                }
                if (isWhite(image.getIntColor(x, y - 1))) {
                    size++;
                }
                if (isWhite(image.getIntColor(x + 1, y - 1))) {
                    size++;
                }
                if (isWhite(image.getIntColor(x + 1, y + 1))) {
                    size++;
                }
                if (size >= 5) {
                    image.setIntColor(x, y, white);
                }
            }
        }

        for (int y = 1; y < h - 1; y++) {
            int x = w - 1;
            if (isBlack(image.getIntColor(x, y))) {
                int size = 0;
                if (isWhite(image.getIntColor(x - 1, y))) {
                    size++;
                }
                if (isWhite(image.getIntColor(x, y + 1))) {
                    size++;
                }
                if (isWhite(image.getIntColor(x, y - 1))) {
                    size++;
                }
                //斜上下为空时，去掉此点
                if (isWhite(image.getIntColor(x - 1, y + 1))) {
                    size++;
                }
                if (isWhite(image.getIntColor(x - 1, y - 1))) {
                    size++;
                }
                if (size >= 5) {
                    image.setIntColor(x, y, white);
                }
            }
        }

        //降噪，以1个像素点为单位
        for (int y = 1; y < h - 1; y++) {
            for (int x = 1; x < w - 1; x++) {
                if (isBlack(image.getIntColor(x, y))) {
                    int size = 0;
                    //上下左右均为空时，去掉此点
                    if (isWhite(image.getIntColor(x - 1, y))) {
                        size++;
                    }
                    if (isWhite(image.getIntColor(x + 1, y))) {
                        size++;
                    }
                    //上下均为空时，去掉此点
                    if (isWhite(image.getIntColor(x, y + 1))) {
                        size++;
                    }
                    if (isWhite(image.getIntColor(x, y - 1))) {
                        size++;
                    }
                    //斜上下为空时，去掉此点
                    if (isWhite(image.getIntColor(x - 1, y + 1))) {
                        size++;
                    }
                    if (isWhite(image.getIntColor(x + 1, y - 1))) {
                        size++;
                    }
                    if (isWhite(image.getIntColor(x + 1, y + 1))) {
                        size++;
                    }
                    if (isWhite(image.getIntColor(x - 1, y - 1))) {
                        size++;
                    }
                    if (size >= 8) {
                        image.setIntColor(x, y, white);
                    }
                }
            }
        }

        return image;
    }

    /**
     * 是否是黑色
     *
     * @param colorInt 颜色值
     * @return 是否是黑色
     */
    public static boolean isBlack(int colorInt) {
        Color color = new Color(colorInt);
        return color.getRed() + color.getGreen() + color.getBlue() <= 300;
    }

    /**
     * 是否是白色
     *
     * @param colorInt
     * @return
     */
    public static boolean isWhite(int colorInt) {
        Color color = new Color(colorInt);
        return color.getRed() + color.getGreen() + color.getBlue() > 300;
    }


    /**
     * 分析图片
     *
     * @param origin   原始图片
     * @param binary   灰度处理的图片
     * @param imageOut 输出图片
     */
    private static void transImages(MarvinImage origin, MarvinImage binary, MarvinImage imageOut) {
        TreeMap<Integer, Integer> colorsMap = new TreeMap<>();
        for (int y = 0; y < origin.getHeight(); y++) {
            for (int x = 0; x < origin.getWidth(); x++) {
                int color = origin.getIntColor(x, y);
                colorsMap.put(color, colorsMap.getOrDefault(color, 0) + 1);
            }
        }
        int maxColor = 0, maxSize = 0;
        for (Integer c : colorsMap.keySet()) {
            if (maxSize < colorsMap.get(c)) {
                maxColor = c;
                maxSize = colorsMap.get(c);
            }
        }

        for (int y = 0; y < binary.getHeight(); y++) {
            List<Integer> w = new ArrayList<>(binary.getWidth());
            double yMargin = new BigDecimal(Math.min(binary.getHeight() - y, y)).divide(new BigDecimal(binary.getWidth()), 10, RoundingMode.HALF_UP).doubleValue();
            for (int x = 0; x < binary.getWidth(); x++) {
                int color = binary.getIntColor(x, y);
                double[] hsv = MarvinColorModelConverter.rgbToHsv(new int[]{color});
                if (hsv[0] == 0D && hsv[1] == 0D && hsv[2] == 0D) {
                    w.add(x);
                } else {
                    w.add(-1);
                }
            }
            //获取系统中的连续黑点，并完成噪点的修改
            List<Integer> startLine = getLine(w, binary, y);
            //通体黑色
            if (startLine.size() == binary.getWidth()) {
                for (Integer x : startLine) {
                    //在新的画板上进行绘制
                    imageOut.setIntColor(x, y, maxColor);
                }
            } else {
                boolean st = true;
                for (int i = 0; i < binary.getWidth(); i++) {
                    boolean exists = false;
                    for (Integer x : startLine) {
                        //计算边距，如果在边距 10%以内才进行找点计算
                        double v = new BigDecimal(Math.min(binary.getWidth() - x, x)).divide(new BigDecimal(binary.getWidth()), 10, RoundingMode.HALF_UP).doubleValue();
                        //如果黑色是从头开始的就直接跳过
                        if (x == i) {
                            //从边上就开始变黑
                            if (st || Math.min(y, binary.getHeight()) <10) {
                                exists = true;
                                break;
                            } else if ( v < 0.1 || yMargin < 0.1){
                                //从当前的点开始搜索
                                int i1 = checkPoint(i, y, binary.getWidth(), binary.getHeight(), binary);
                                if (i1 > 0) {
                                    exists = true;
                                    break;
                                }
                            }
                        } else {
                            st = false;
                        }
                    }
                    if (exists) {
                        //在新的画板上进行绘制
                        imageOut.setIntColor(i, y, maxColor);
                    } else {
                        //在新的画板上进行绘制
                        imageOut.setIntColor(i, y, origin.getIntColor(i, y));
                    }
                }

            }
        }
    }

    /**
     * 上下检索一下
     *
     * @param x1
     * @param y1
     * @param width
     * @param height
     * @param binary
     * @return
     */
    private static int checkPoint(int x1, int y1, int width, int height, MarvinImage binary) {
        int startX = Math.min(Math.abs(x1 - STEP), x1), startY = Math.min(Math.abs(y1 - STEP), y1);
        int endX = x1 + Math.min(width - x1, STEP), endY = y1 + Math.min(height - y1, STEP);
        int t = 0, s = 0;
        for (int i = startX; i < endX; i++) {
            for (int j = startY; j < endY; j++) {
                t = t + 1;
                int color = binary.getIntColor(i, j);
                double[] hsv = MarvinColorModelConverter.rgbToHsv(new int[]{color});
                //记录黑色的点的位置
                if (hsv[0] == 0D && hsv[1] == 0D && hsv[2] == 0D) {
                    s = s + 1;
                }
            }
        }
        boolean ex = t < s * 2;
        return ex ? Math.min(width - x1, STEP) : 0;
    }

    /**
     * 获取连续黑色的直线段
     * 中间有非黑色点作为噪点处理掉，补齐整个坐标点
     *
     * @param w
     * @return
     */
    private static List<Integer> getLine(List<Integer> w, MarvinImage binary, int y) {
        Set<Integer> start = new HashSet<>();
        //从前往后搜索
        for (int i = 0; i < w.size(); i++) {
            //向后搜索一个像素，是黑色的
            if (i + 1 < w.size() && Math.abs(w.get(i) - w.get(i + 1)) != 0) {
                if (w.get(i) == -1) {
                    if (i == 0 && w.get(i + 1) != -1) {
                        //向前找一位找补
                        start.add(w.get(i + 1) - 1);
                    } else if (i == 0 && w.get(i + 2) != -1) {
                        //向前找两位找补
                        start.add(w.get(i + 2) - 2);
                    } else if (i - 1 >= 0 && w.get(i - 1) != -1) {
                        //向后找一个找补
                        start.add(w.get(i - 1) + 1);
                    } else if (i - 2 >= 0 && w.get(i - 2) != -1) {
                        //向后找两位找补
                        start.add(w.get(i - 2) + 2);
                    }
                } else {
                    start.add(w.get(i));
                }
            } else if (i + 2 < w.size() && ((Math.abs(w.get(i + 1) - w.get(i + 2)) != 0) || Math.abs(w.get(i) - w.get(i + 2)) != 0)) {
                //向后搜索两个像素是黑色的
                if (w.get(i) == -1) {
                    if (i == 0 && w.get(i + 1) != -1) {
                        //向前找一位找补
                        start.add(w.get(i + 1) - 1);
                    } else if (i == 0 && w.get(i + 2) != -1) {
                        //向前找两位找补
                        start.add(w.get(i + 2) - 2);
                    } else if (i - 1 >= 0 && w.get(i - 1) != -1) {
                        //向后找一个找补
                        start.add(w.get(i - 1) + 1);
                    } else if (i - 2 >= 0 && w.get(i - 2) != -1) {
                        //向后找两位找补
                        start.add(w.get(i - 2) + 2);
                    }
                } else {
                    start.add(w.get(i));
                }
            }
        }

        //从 后往前搜索
        for (int i = w.size() - 1; i >= 0; i--) {
            if (i - 1 >= 0 && Math.abs(w.get(i) - w.get(i - 1)) != 0) {
                if (w.get(i) == -1) {
                    if (i + 1 == w.size() && w.get(i - 1) != -1) {
                        //最后一位，朝后找补一位
                        start.add(w.get(i - 1) + 1);
                    } else if (i + 1 == w.size() && w.get(i - 2) != -1) {
                        //最后两位，朝后找补一位
                        start.add(w.get(i - 2) + 2);
                    } else if (i + 1 < w.size() && w.get(i + 1) != -1) {
                        //往后找补补齐
                        start.add(w.get(i + 1) - 1);
                    } else if (i + 2 < w.size() && w.get(i + 2) != -1) {
                        //往后找补补齐
                        start.add(w.get(i + 2) - 2);
                    }
                } else {
                    start.add(w.get(i));
                }
            } else if (i - 2 >= 0 && ((Math.abs(w.get(i - 1) - w.get(i - 2)) != 0) || Math.abs(w.get(i) - w.get(i - 2)) != 0)) {
                if (w.get(i) == -1) {
                    if (i + 1 == w.size() && w.get(i - 1) != -1) {
                        //最后一位，朝后找补一位
                        start.add(w.get(i - 1) + 1);
                    } else if (i + 1 == w.size() && w.get(i - 2) != -1) {
                        //最后两位，朝后找补一位
                        start.add(w.get(i - 2) + 2);
                    } else if (i + 1 < w.size() && w.get(i + 1) != -1) {
                        //往后找补补齐
                        start.add(w.get(i + 1) - 1);
                    } else if (i + 2 < w.size() && w.get(i + 2) != -1) {
                        //往后找补补齐
                        start.add(w.get(i + 2) - 2);
                    }
                } else {
                    start.add(w.get(i));
                }
            } else {
                break;
            }
        }
        return start.parallelStream().collect(Collectors.toList());
    }

    /**
     * 扣绿色
     *
     * @param imageIn
     * @param imageOut
     */
    private void greenToTransparency(MarvinImage imageIn, MarvinImage imageOut) {

        for (int y = 0; y < imageIn.getHeight(); y++) {
            for (int x = 0; x < imageIn.getWidth(); x++) {

                int color = imageIn.getIntColor(x, y);
                int r = imageIn.getIntComponent0(x, y);
                int g = imageIn.getIntComponent1(x, y);
                int b = imageIn.getIntComponent2(x, y);

                double[] hsv = MarvinColorModelConverter.rgbToHsv(new int[]{color});

                if (hsv[0] == 0 && hsv[1] == 0 && hsv[2] == 0) {
                    imageOut.setIntColor(x, y, 0, 127, 127, 127);
                } else {
                    imageOut.setIntColor(x, y, color);
                }

            }
        }
    }


    /**
     * 过度
     *
     * @param image
     */
    private void reduceGreen(MarvinImage image) {
        for (int y = 0; y < image.getHeight(); y++) {
            for (int x = 0; x < image.getWidth(); x++) {
                int r = image.getIntComponent0(x, y);
                int g = image.getIntComponent1(x, y);
                int b = image.getIntComponent2(x, y);
                int color = image.getIntColor(x, y);
                double[] hsv = MarvinColorModelConverter.rgbToHsv(new int[]{color});

                if (hsv[0] == 0 && hsv[1] == 0 && hsv[2] == 0) {
                    if ((r * b) != 0 && (g * g) / (r * b) > 1.5) {
                        image.setIntColor(x, y, 255, (int) (r * 1.4), (int) g, (int) (b * 1.4));
                    } else {
                        image.setIntColor(x, y, 255, (int) (r * 1.2), g, (int) (b * 1.2));
                    }
                }
            }
        }
    }

    public static void main(String[] args) {
        new ChromaToTransparency();
    }
}
